package com.shareware.common.secure.rsa

import android.os.Build
import android.util.Base64
import androidx.annotation.RequiresApi
import com.shareware.common.utils.log
import java.security.KeyFactory
import java.security.PrivateKey
import java.security.PublicKey
import java.security.Signature
import java.security.interfaces.RSAPrivateKey
import java.security.interfaces.RSAPublicKey
import java.security.spec.MGF1ParameterSpec
import java.security.spec.PKCS8EncodedKeySpec
import java.security.spec.PSSParameterSpec
import java.security.spec.X509EncodedKeySpec

/**
 * desc: 功能描述
 * email: mobiledeveloper@qq.com
 */
class RsaSign private constructor() {
    companion object {
        private const val TAG = "RsaSign"
        private const val ALGORITHM_NAME = "RSA"
        private const val ALGORITHM_PASS_NAME = "SHA256WithRSA/PSS"
        private const val KEY_LENGTH = 2048

        @RequiresApi(api = Build.VERSION_CODES.M)
        fun sign(content: String, privateKey: String): String {
            return if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) {
                Base64.encodeToString(
                    sign(
                        content.toByteArray(),
                        getPrivateKey(privateKey)
                    ), Base64.DEFAULT
                )
            } else {
                log(TAG, "sdk version is low")
                ""
            }
        }

        /**
         * @param srcData 原始数据
         * @param sign 签名
         * @param publicKey 公钥
         * @return boolean 是否验签通过
         */
        @RequiresApi(api = Build.VERSION_CODES.M)
        fun verifySign(srcData: String, sign: String, publicKey: String): Boolean {
            return if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1 && srcData.isNotEmpty()
                && sign.isNotEmpty() && publicKey.isNotEmpty()
            ) {
                verifySign(
                    srcData.toByteArray(),
                    Base64.decode(sign, Base64.DEFAULT),
                    getPublicKey(publicKey)
                )
            } else {
                log(TAG, "sdk version is low or param is null")
                false
            }
        }

        private fun verifySign(
            srcBytes: ByteArray,
            signBytes: ByteArray,
            publicKey: PublicKey?
        ): Boolean {
            return if (srcBytes.isNotEmpty() && signBytes.isNotEmpty() && isPublicKeyLengthRight(
                    publicKey as? RSAPublicKey
                )
            ) {
                try {
                    val signature = Signature.getInstance(ALGORITHM_PASS_NAME)
                    signature.setParameter(
                        PSSParameterSpec(
                            "SHA-256", "MGF1",
                            MGF1ParameterSpec.SHA256, 32, 1
                        )
                    )
                    signature.initVerify(publicKey)
                    signature.update(srcBytes)
                    signature.verify(signBytes)
                } catch (ignore: Exception) {
                    false
                }
            } else {
                false
            }
        }

        private fun sign(contentBytes: ByteArray, privateKey: PrivateKey?): ByteArray {
            return if (contentBytes.isNotEmpty() && isPrivateKeyLengthRight(privateKey as? RSAPrivateKey)) {
                try {
                    val signature = Signature.getInstance(ALGORITHM_PASS_NAME)
                    signature.setParameter(
                        PSSParameterSpec(
                            "SHA-256", "MGF1",
                            MGF1ParameterSpec.SHA256, 32, 1
                        )
                    )
                    signature.initSign(privateKey)
                    signature.update(contentBytes)
                    signature.sign()
                } catch (ignore: Exception) {
                    byteArrayOf()
                }
            } else {
                byteArrayOf()
            }
        }

        private fun isPublicKeyLengthRight(publicKey: RSAPublicKey?): Boolean {
            return if (publicKey == null) {
                false
            } else {
                publicKey.modulus.bitLength() >= KEY_LENGTH
            }
        }

        private fun isPrivateKeyLengthRight(privateKey: RSAPrivateKey?): Boolean {
            return if (privateKey == null) {
                false
            } else {
                privateKey.modulus.bitLength() >= KEY_LENGTH
            }
        }

        private fun getPublicKey(base64Str: String): PublicKey? {
            return try {
                val keyString = Base64.decode(base64Str, Base64.DEFAULT)
                val keyFactory = KeyFactory.getInstance(ALGORITHM_NAME)
                val x509EncodedKeySpec = X509EncodedKeySpec(keyString)
                keyFactory.generatePublic(x509EncodedKeySpec)
            } catch (ignore: Exception) {
                log(TAG, "${ignore.message}")
                null
            }
        }

        private fun getPrivateKey(base64Str: String): PrivateKey? {
            return try {
                val keyString = Base64.decode(base64Str, Base64.DEFAULT)
                val keyFactory = KeyFactory.getInstance(ALGORITHM_NAME)
                val pkcS8EncodedKeySpec = PKCS8EncodedKeySpec(keyString)
                keyFactory.generatePrivate(pkcS8EncodedKeySpec)
            } catch (ignore: Exception) {
                log(TAG, "${ignore.message}")
                null
            }
        }
    }
}